package snafu.framework.actions;

import java.util.Arrays;
import java.util.List;

import snafu.framework.agents.AgentState;
import snafu.framework.objects.WorldObject;
import snafu.framework.view.Access;
import snafu.framework.view.ViewFactory;
import snafu.framework.worlds.WorldMap;

public abstract class IntentionItself<A extends AbstractAction> implements Intention<A>,
		ViewFactory<IntentionView<A>> {

	/** The access type. */
	private Access privacy;

	/**
	 * Returns the access type.
	 * 
	 * @return the access type.
	 */
	public Access getPrivacy() {
		return privacy;
	}

	/**
	 * Creates a new intention with the given access tpye. This will be used to create views.
	 * 
	 * @param privacy
	 *            the access type
	 */
	public IntentionItself(Access privacy) {
		this.privacy = privacy;
	}
	
	@Override
	public boolean isApplicable(WorldMap perception, AgentState state, List<WorldObject> targets) {
		
		// check whether agent is able to execute action
		List<Intention<?>> agentIntentions = ((AgentState) state).getIntentions();

		if (!agentIntentions.contains(this))
			return false;
		
		// check whether action is applicable on target
		return checkApplicability(perception, state, targets);
	}
	
	@Override
	public boolean isApplicable(WorldMap perception, AgentState agent, WorldObject... targets) {
		return isApplicable(perception, agent, Arrays.asList(targets));
	}

	/**
	 * Indicates whether the action created by this intention is applicable given the current state
	 * of the world the state of the agent and the list of targets for the action. Must be
	 * implemented by all subclasses and will be called by the
	 * {@link #isApplicable(WorldMap, AgentState, List)} method.
	 * 
	 * @param perception
	 *            the world
	 * @param agent
	 *            the agent state
	 * @param targets
	 *            the targets
	 * @return true if the action is applicable
	 */
	protected abstract boolean checkApplicability(WorldMap perception, AgentState agent, List<WorldObject> targets);
	
	/**
	 * Two intentions are equal, if they share an interface, which can is assignable from
	 * {@link Intention}, that both intentions can be assigned to.
	 */
	@Override
	public boolean equals(Object o) {
		if (!(o instanceof Intention<?>))
			return false;
		
		Class<?>[] interfaces = this.getClass().getInterfaces();
		
		for (Class<?> clazz : interfaces) {
			if (clazz.isAssignableFrom(o.getClass()) && clazz.isAssignableFrom(this.getClass())
					&& Intention.class.isAssignableFrom(clazz))
				return true;
		}
		
		return false;
	}
	
	@Override
	public A createAction(WorldObject... targets) {
		return createAction(Arrays.asList(targets));
	}
	
}